package ast;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;

public class SymbolTable {

	private String name;
	private SymbolTable parent;
	private ArrayList<SymbolTable> children;
	private HashMap<String, Object> table;
	private int nextChild;

	public SymbolTable(String name){
		this.name = name;
		table = new HashMap<String, Object>();
		children = new ArrayList<SymbolTable>();
		parent = null;
		resetNextChild();
	}

	public String getName(){
		return name;
	}

	public void setParent(SymbolTable parent){
		this.parent=parent;
	}

	public void resetNextChild() {
		for (SymbolTable sTable:children)
		{
			sTable.resetNextChild();
		}
		nextChild = 0;
	}

	@SuppressWarnings("unchecked")
	public void put(String key, String value)
	{
		ArrayList tableValue = new ArrayList();
		if (!table.containsKey(key))
		{
			tableValue.add(value);
			table.put(key, tableValue);
		}
		else if (table.containsKey(key) && key.matches(".*\\(\\)$"))
		{
			String[] entryValueTemp;
			String entryTypeExpression;
			String[] tableValueTemp;
			String tableTypeExpression;
			
			entryValueTemp = splitExpression(value, ":");
			entryTypeExpression = getTypeExpression(entryValueTemp);
			tableValue = (ArrayList) table.get(key);
			for (Object tValue: tableValue)
			{
				tableValueTemp = splitExpression((String)tValue,":");
				tableTypeExpression = getTypeExpression(tableValueTemp);
				if (entryTypeExpression.equals(tableTypeExpression))
				{
					System.out.println("Error! Method " + key + " with parameter "
						+ entryTypeExpression + " has already been declared in this scope.");
					return;
				}
			}
			tableValue.add(value);
			table.put(key, tableValue);
		}
		else
		{
			System.out.println("Error! Variable name " + key + " has already been " +
								"declared in this scope.");
		}
	}

	public void put(String key, SymbolTable value) {
		table.put(key, value);
	}

	public void editVar(String key, String modifier) {
		ArrayList tableValue;
		String value;
		String returnType;
		String[] temp;

		tableValue = (ArrayList)get(key);
		value = (String)tableValue.get(0);
		temp = splitExpression(value,":");
		returnType = getReturnType(temp);
		value = ":" + returnType + ":" + modifier;
		table.remove(key);
		put(key, value);
	}

	public Object get(String key) {
		return table.get(key);
	}

	public Set<String> getKeys() {
		return table.keySet();
	}

	@SuppressWarnings("unchecked")
	public void loopList()
	{
		String tableName;
		ArrayList<String> value = new ArrayList<String>();
		String[] temp;
		SymbolTable globalValues;

		for (SymbolTable sTable:children)
		{
			tableName = sTable.getName();
			globalValues = new SymbolTable(tableName);
			for (String key:sTable.getKeys())
			{
				value = (ArrayList<String>)sTable.get(key);
				for (String s:value)
				{
					temp = splitExpression(s,":");
					if (getModifier(temp).equals("true"))
					{
						String tableValue = getTableValue(temp);
						globalValues.put(key, tableValue);
					}
				}
			}
			table.put(tableName, globalValues);
		}
	}

	public String[] splitExpression(String s, String symbol)
	{
		String[] temp = s.split(symbol);
		return temp;
	}

    public String getTypeExpression(String[] temp) {
        String typeExpression = temp[0].toString();
        return typeExpression;
    }

    public String getReturnType(String[] temp) {
        String returnType = temp[1].toString();
        return returnType;
    }

    public String getModifier(String[] temp) {
        String modifier = temp[2].toString();
        return modifier;
    }

    public String getTableValue(String[] temp) {
    	String tableValue = temp[0].toString() + ":" + temp[1].toString();
    	return tableValue;
    }

    public Object getKeyType(String id) {
        SymbolTable currTable = this;
        SymbolTable parentTable = currTable.parent;

        while (!currTable.exists(id)) {
            if (parentTable != null) {
                currTable = parentTable;
                parentTable = currTable.parent;
            } else {
                return null;
            }
        }
        return currTable.get(id);
    }

	public SymbolTable beginScope(String name) {
		SymbolTable newTable = new SymbolTable(name);
		newTable.setParent(this);
		children.add(newTable);
		return newTable;
	}

	public SymbolTable endScope() {
		parent.nextChild++;
		return parent;
	}

    public SymbolTable traverse() {
    	return children.get(nextChild);
    }

    public Boolean scopeCheck(String id) {
        SymbolTable currTable = this;
        SymbolTable parentTable = currTable.parent;

        while (!currTable.exists(id)) {
            if (parentTable != null) {
                currTable = parentTable;
                parentTable = currTable.parent;
            } else {
                return false;
            }
        }
        return true;
    }

    public Boolean exists(String id) {
        return table.containsKey(id);
    }
}